package com.utils;

import javax.servlet.http.HttpServletRequest;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Set;

public class RequestToBeanConvertor {
    //classType传过来的什么类型<T>就返回什么样的对象
    public static <T> T convert(HttpServletRequest request,Class<T> clzType)throws Exception{
        T bean=clzType.newInstance();
        //拿到所有提交的参数，key=name，value=值数组，提交的值
        Map<String,String[]> parameterMap=request.getParameterMap();
        //获取所有的key值，也就是name值，request.getParameterMap();这个方式也是一样的可以取name值
        Set<String> names=parameterMap.keySet();
        //循环所有的名称
        for(String name:names){
            try{
                //通过提交的name拿到对象中的属性，拿到字段
                PropertyDescriptor pd=new PropertyDescriptor(name,clzType);
                //字段类型
                Class<?> propertyType=pd.getPropertyType();
                //settet方法
                Method writhMethod=pd.getWriteMethod();
                //拿到值
                String[] values=parameterMap.get(name);
                Object value=null;
                if(propertyType.isArray()){  //判断是否是数组
                    //拿到内部的元素的类型
                   Class<?> componentType= propertyType.getComponentType();
                //数组对象
                    value= Array.newInstance(componentType,values.length);
                    for (int i = 0; i <values.length ; i++) {
                      Object value1=parse(values[i],componentType);
                      //把转换后的值传入数组
                        Array.set(value,i,value1);
                    }
                }else {
                    //如果不是数组，取第一个就可以了
                    //取出第一个
                    String strValue=values[0];
                    //转换类型
                value=parse(strValue,propertyType);
                }
                //setter注入值
                writhMethod.invoke(bean,value);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return  bean;
    }


    //把字符串转成某个类型,Class<?> type是什么类型，value就转成什么类型
    public static Object parse(String value,Class<?> type){
        //如果类型本身就是字符串，直接返回
        if(type==String.class){
           return value;
        }
      //如果是基础数据类型，就转为包装数据类型，如果是引用数据类型或是其他数据类型，则不处理
       type=toPackageClz(type);
        //
        if(type.getName().startsWith("java.lang.")||type.getName().startsWith("java.math")){
            try{
                if(type==Character.class){
                    return new Character(value.charAt(0));
                }
                Constructor<?> constructor=type.getConstructor(String.class);
                return constructor.newInstance(value);
            }catch (Exception e){
             e.printStackTrace();
            }
        }
        //如果是时间类型
        if(type == Date.class){
            SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            try{
                return format.parse(value);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        throw new RuntimeException(value+"无法转换成"+type.getName()+"类型");
    }

    //把基础数据类型转成包装类型
    public static Class<?> toPackageClz(Class<?> type){
        //判断是不是基础数据类型
        if(type.isPrimitive()) {
//            int ----->Integer
//            char----->Character    就这两个基础数据类型不是首字母大写，其他的都是首字母大写
            if (type == int.class) {
                type = Integer.class;
            } else if (type == char.class) {
                type = Character.class;
            } else {
                //其他数据类型，首字母大写就行
                String typeName = type.getSimpleName(); //拿到名字
                try {
                    // 例：float--------> Float
                    //拿到这个类型的名字，必须要加包的全路径名，截取第一个，转为大写，后面的不变，进行拼接
                    type = Class.forName("java.lang" + typeName.substring(0, 1).toUpperCase() + typeName.substring(1));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return type;
    }

}
